#ifndef OPP_H_
#define OPP_H_

#include <stdio.h>
#include <stdint.h>
#include <sys/time.h>
#include <pthread.h>
#include <stdarg.h>

#include "config.h"

#include "debug_functions.h"
#include "chrono_functions.h"
#include "token_node.h"
#include "token_node_stack.h"
#include "precedence_stack.h"
#include "reduction_list.h"
#include "opplib.h"
#include "prec_matrix.h"
#include "grammar.h"
#include "lex.h"

/**
 * \brief opp_result is the structure used to store the result of the parsing process.
 */
typedef struct opp_result {
  unsigned short result; /**< 0 if the axiom is reached, 1 if the axiom is not reached, 2 if the string does not belong to the language, else 3. */
#if TEST
  uint32_t reallocations; /**< Number of node tree reallocations performed. */
  uint32_t max_prec; /**< Maximum number of elements contained in prec_stack. */
  uint32_t list_realloc; /**< Number of reduction_list reallocations performed. */
  uint32_t list_max; /**< Maximum number of reduction_list elements. */
#endif
} opp_result;

/**
 * \brief opp_stack_el is the structure used to store information regarding an element of the stack.
 */
typedef struct opp_stack_el {
  opp_list *tokens; /**< Either a terminal token or the set of nonterminal tokens obtained after a reduction. */
  prec_rel prec; /**< The precedence relation with the next terminal token. */
  opp_node *value; /**< The semantic value of the element. */
} opp_stack_el;

inline uint8_t rewrite_to_axiom(gr_token token, parsing_ctx *ctx);

inline token_node *get_last_terminal(precedence_stack *prec_stack, token_node *c_prev, token_node *current_list_pos, token_node *list_end);

inline void get_next_token(token_node **current_list_pos, token_node **prev_list_pos, token_node *list_end, token_node *c_next, uint8_t *TERMINATOR, const char * const *gr_token_name);

inline prec_rel set_precedence(token_node *current_list_pos, precedence_stack *prec_stack, token_node *c_prev, token_node *list_end, parsing_ctx *ctx);

inline int8_t init_parser(opp_chrono *chrono, uint8_t id, opp_result **result, token_node_stack *stack, parsing_ctx *ctx);

inline void return_wip(opp_result *result, opp_chrono *chrono, token_node *current_list_pos, precedence_stack *prec_stack, reduction_list *red_list, reduction_list *temp_red_list, token_node_stack *stack, parsing_ctx *ctx);

inline void return_error(opp_result *result, opp_chrono *chrono, token_node *current_list_pos, precedence_stack *prec_stack, reduction_list *red_list, reduction_list *temp_red_list, token_node_stack *stack, parsing_ctx *ctx);

inline void return_not_recognized(opp_result *result, opp_chrono *chrono, token_node *current_list_pos, precedence_stack *prec_stack, reduction_list *red_list, reduction_list *temp_red_list, token_node_stack *stack, parsing_ctx *ctx);

inline void return_success(opp_result *result, opp_chrono *chrono, token_node *current_list_pos, precedence_stack *prec_stack, reduction_list *red_list, reduction_list *temp_red_list, token_node_stack *stack, parsing_ctx *ctx);

inline void reduction_step(reduction_list *temp_reduction_list, uint32_t red_list_node, token_node *list_itr, parsing_ctx *ctx);

inline void get_next_reduction_token(token_node **list_itr);

inline void perform_rewrite(token_node *rewrite_pos, gr_token current, gr_token goal, token_node_stack *stack, parsing_ctx *ctx);

inline void call_semantics(opp_chrono *chrono, reduction_list *reduction_list, precedence_stack *prec_stack, token_node *prev_list_pos, token_node_stack *stack, parsing_ctx *ctx);

inline uint32_t get_son_with(const uint16_t *tree, uint32_t offset, gr_token label);

opp_result *opp_parse(token_node *c_prev, token_node *c_next, uint8_t id, token_node *list_begin, token_node *list_end, parsing_ctx *ctx);

#endif
